#!/usr/bin/env python3
"""
QQ语音文件处理工具 - 简化版
专门优化鼠标点击播放语音的捕获效率
"""
import os
import time
import psutil
import subprocess
import logging
import platform
from pathlib import Path
from datetime import datetime
from typing import Set, Optional, List, Dict
import re
import shutil
import argparse
import threading
from concurrent.futures import ThreadPoolExecutor, as_completed
import tempfile
import hashlib
import json
from tqdm import tqdm
import sys
import select
import termios
import tty
import fcntl

# 简化日志配置
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('qq_audio_tool.log', encoding='utf-8')
    ]
)

class Config:
    """配置参数类"""
    DEFAULT_OUTPUT_ROOT = Path.home() / "Documents/lesson/output"
    MAX_WORKERS = max(1, (os.cpu_count() or 1) // 2)
    SILK_DIR = Path(__file__).parent / "silk"
    CACHE_DIR = Path("~/.cache/qq_voice_converter").expanduser()
    HASH_CACHE_EXPIRY = 86400
    
    # 文件捕获配置
    CAPTURE_CHECK_INTERVAL = 0.5
    PROCESS_CHECK_INTERVAL = 5
    MAX_CAPTURE_DELAY = 2.0
    
    def __init__(self):
        self.DEFAULT_OUTPUT_ROOT.mkdir(exist_ok=True, parents=True)
        self.CACHE_DIR.mkdir(exist_ok=True, parents=True)

config = Config()

class KeyboardListener:
    """简化键盘监听器"""
    
    def __init__(self):
        self._running = False
        self._thread = None
        self._current_key = None
        self._old_term = None
        self._old_flags = None
        
    def _nonblocking_input_setup(self):
        """设置非阻塞输入"""
        try:
            self._old_term = termios.tcgetattr(sys.stdin)
            tty.setraw(sys.stdin.fileno())
            
            self._old_flags = fcntl.fcntl(sys.stdin, fcntl.F_GETFL)
            fcntl.fcntl(sys.stdin, fcntl.F_SETFL, self._old_flags | os.O_NONBLOCK)
        except Exception as e:
            logging.debug(f"设置非阻塞输入失败: {e}")
    
    def _nonblocking_input_cleanup(self):
        """恢复终端设置"""
        try:
            if self._old_term:
                termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self._old_term)
            if self._old_flags is not None:
                fcntl.fcntl(sys.stdin, fcntl.F_SETFL, self._old_flags)
        except Exception as e:
            logging.debug(f"恢复终端设置失败: {e}")
    
    def _listen_loop(self):
        """监听循环"""
        self._nonblocking_input_setup()
        try:
            while self._running:
                try:
                    if select.select([sys.stdin], [], [], 0.1)[0]:
                        key = sys.stdin.read(1)
                        if key:
                            self._current_key = key.lower()
                except (IOError, ValueError):
                    pass
                time.sleep(0.05)
        finally:
            self._nonblocking_input_cleanup()
    
    def start(self):
        """开始监听"""
        if self._running:
            return
        self._running = True
        self._current_key = None
        self._thread = threading.Thread(target=self._listen_loop, daemon=True)
        self._thread.start()
    
    def stop(self):
        """停止监听"""
        self._running = False
        if self._thread:
            self._thread.join(timeout=1.0)
    
    def get_key(self):
        """获取当前按下的键"""
        key = self._current_key
        self._current_key = None
        return key
    
    def has_key(self, key_char):
        """检查是否按下了指定键"""
        return self._current_key == key_char.lower()

class QQVoiceConverter:
    """QQ语音转换器简化版"""
    
    def __init__(self):
        self._converted_files_lock = threading.Lock()
        self.converted_files: List[Path] = []
        self.decoder_path: Optional[Path] = None
        self._hash_cache: Dict[str, tuple] = self._load_hash_cache()
        self._files_to_delete: Set[Path] = set()
        self._hash_cache_lock = threading.Lock()

    def _load_hash_cache(self) -> Dict[str, tuple]:
        """加载文件哈希缓存"""
        cache_file = config.CACHE_DIR / "file_hashes.json"
        if cache_file.exists():
            try:
                with open(cache_file, "r", encoding="utf-8") as f:
                    cache_data = json.load(f)
                    current_time = time.time()
                    valid_cache = {}
                    for path, (hash_val, timestamp) in cache_data.items():
                        if current_time - timestamp < config.HASH_CACHE_EXPIRY:
                            valid_cache[path] = (hash_val, timestamp)
                    return valid_cache
            except Exception as e:
                logging.warning(f"加载哈希缓存失败: {e}")
        return {}

    def _save_hash_cache(self) -> None:
        """保存文件哈希缓存"""
        try:
            with self._hash_cache_lock:
                cache_file = config.CACHE_DIR / "file_hashes.json"
                with open(cache_file, "w", encoding="utf-8") as f:
                    json.dump(self._hash_cache, f, ensure_ascii=False, indent=2)
        except Exception as e:
            logging.warning(f"保存哈希缓存失败: {e}")

    def _get_file_hash(self, file_path: Path) -> str:
        """计算文件哈希值"""
        try:
            file_stat = file_path.stat()
            file_size = file_stat.st_size
            file_mtime = file_stat.st_mtime
            
            meta_key = f"{file_size}_{file_mtime:.4f}"
            
            cache_key = str(file_path.resolve())
            with self._hash_cache_lock:
                if cache_key in self._hash_cache:
                    cached_hash, cached_time = self._hash_cache[cache_key]
                    if time.time() - cached_time < config.HASH_CACHE_EXPIRY:
                        return cached_hash
            
            hash_obj = hashlib.md5()
            with open(file_path, "rb") as f:
                while chunk := f.read(4096):
                    hash_obj.update(chunk)
            file_hash = hash_obj.hexdigest()
            
            with self._hash_cache_lock:
                self._hash_cache[cache_key] = (file_hash, time.time())
            
            return file_hash
        except (FileNotFoundError, PermissionError) as e:
            logging.warning(f"无法计算文件哈希 {file_path}: {e}")
            return ""

    def compile_decoder(self) -> Path:
        """编译Silk解码器"""
        decoder_name = "decoder.exe" if platform.system() == "Windows" else "decoder"
        decoder = config.SILK_DIR / decoder_name
        
        if decoder.exists():
            try:
                if platform.system() == "Windows":
                    if decoder.suffix.lower() == ".exe":
                        return decoder
                else:
                    if os.access(decoder, os.X_OK):
                        return decoder
            except Exception:
                pass
        
        print("正在编译Silk解码器...")
        try:
            if not config.SILK_DIR.exists():
                print("Silk目录不存在，尝试克隆仓库...")
                result = subprocess.run(
                    ["git", "clone", "https://github.com/kn007/silk-v3-decoder.git", str(config.SILK_DIR)],
                    capture_output=True,
                    text=True
                )
                if result.returncode != 0:
                    logging.error(f"克隆仓库失败: {result.stderr}")
                    raise RuntimeError("Silk解码器仓库克隆失败")
            
            if platform.system() == "Windows":
                result = subprocess.run(
                    ["nmake", "/F", "Makefile.msvc"], 
                    cwd=config.SILK_DIR, 
                    capture_output=True,
                    text=True
                )
                if result.returncode != 0:
                    result = subprocess.run(
                        ["gcc", "*.c", "-o", "decoder"], 
                        cwd=config.SILK_DIR, 
                        capture_output=True,
                        text=True
                    )
            else:
                result = subprocess.run(
                    ["make", "clean"], 
                    cwd=config.SILK_DIR, 
                    capture_output=True,
                    text=True
                )
                result = subprocess.run(
                    ["make"], 
                    cwd=config.SILK_DIR, 
                    capture_output=True,
                    text=True
                )
            
            if result.returncode != 0:
                logging.error(f"编译错误输出: {result.stderr}")
                raise RuntimeError("解码器编译失败")
            
            if not decoder.exists():
                possible_decoders = list(config.SILK_DIR.glob("decoder*"))
                if possible_decoders:
                    decoder = possible_decoders[0]
                else:
                    raise FileNotFoundError("编译成功但未找到解码器可执行文件")
            
            if platform.system() != "Windows":
                os.chmod(decoder, 0o755)
            
            print("Silk解码器编译完成")
            return decoder
        
        except Exception as e:
            print(f"编译失败: {e}")
            raise

    @staticmethod
    def check_ffmpeg() -> bool:
        """检查ffmpeg是否安装"""
        try:
            subprocess.run(
                ["ffmpeg", "-version"], 
                stdout=subprocess.DEVNULL, 
                stderr=subprocess.DEVNULL, 
                check=True
            )
            return True
        except Exception:
            return False

    def convert_single_file(self, file_path: Path) -> Optional[Path]:
        """转换单个音频文件"""
        try:
            if not file_path.exists():
                logging.warning(f"文件不存在，跳过: {file_path}")
                return None
            
            output_dir = file_path.parent
            output_mp3 = output_dir / f"{file_path.stem}.mp3"
            
            file_hash = self._get_file_hash(file_path)
            if not file_hash:
                return None
            
            if output_mp3.exists():
                cache_key = str(file_path.resolve())
                with self._hash_cache_lock:
                    if cache_key in self._hash_cache and self._hash_cache[cache_key][0] == file_hash:
                        with self._converted_files_lock:
                            self.converted_files.append(output_mp3)
                        if file_path.exists():
                            self._files_to_delete.add(file_path)
                        return output_mp3
            
            with tempfile.TemporaryDirectory() as tmp_dir:
                temp_pcm = Path(tmp_dir) / "audio.pcm"
                
                result = subprocess.run(
                    [str(self.decoder_path), str(file_path), str(temp_pcm)],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    universal_newlines=True
                )
                if result.returncode != 0:
                    logging.warning(f"解码失败 {file_path.name}: {result.stderr}")
                    return None
                
                if not temp_pcm.exists():
                    logging.warning(f"PCM文件未生成: {file_path.name}")
                    return None
                
                temp_mp3 = output_mp3.with_suffix('.temp.mp3')
                result = subprocess.run(
                    ["ffmpeg", "-y", "-hide_banner", "-loglevel", "error",
                     "-f", "s16le", "-ar", "24000", "-ac", "1",
                     "-i", str(temp_pcm), "-b:a", "32k", "-filter:a", "volume=1.5",
                     str(temp_mp3)],
                    capture_output=True,
                    text=True
                )
                
                if result.returncode != 0:
                    logging.warning(f"FFmpeg转换失败 {file_path.name}: {result.stderr}")
                    if temp_mp3.exists():
                        temp_mp3.unlink()
                    return None
                
                if temp_mp3.exists():
                    with open(temp_mp3, 'rb') as f:
                        os.fsync(f.fileno())
                    temp_mp3.rename(output_mp3)
                else:
                    logging.warning(f"临时MP3文件未生成: {temp_mp3}")
                    return None
            
            cache_key = str(file_path.resolve())
            with self._hash_cache_lock:
                self._hash_cache[cache_key] = (file_hash, time.time())
            with self._converted_files_lock:
                self.converted_files.append(output_mp3)
            
            if file_path.exists():
                self._files_to_delete.add(file_path)
            return output_mp3
        
        except subprocess.CalledProcessError as e:
            logging.warning(f"转换失败 {file_path.name}: {e.stderr if hasattr(e, 'stderr') else str(e)}")
        except Exception as e:
            logging.warning(f"处理 {file_path.name} 时出错: {str(e)}")
        return None

    def _delete_processed_files(self) -> int:
        """删除已成功转换的原文件"""
        deleted_count = 0
        files_to_remove = []
        
        for file_path in self._files_to_delete:
            try:
                if file_path.exists():
                    file_path.unlink()
                    deleted_count += 1
                    print(f"已删除原文件: {file_path.name}")
                    files_to_remove.append(file_path)
            except Exception as e:
                logging.warning(f"删除原文件失败 {file_path}: {e}")
        
        for file_path in files_to_remove:
            self._files_to_delete.discard(file_path)
        
        return deleted_count

    def process_conversion(self, target_dir: Path) -> bool:
        """处理目录下的音频文件转换"""
        if not self.check_ffmpeg():
            install_guide = {
                "Darwin": "请使用brew install ffmpeg安装",
                "Windows": "请从https://ffmpeg.org/下载并添加到系统PATH",
                "Linux": "请使用sudo apt install ffmpeg安装"
            }.get(platform.system(), "请安装ffmpeg并添加到系统PATH")
            print(f"未找到ffmpeg。{install_guide}")
            return False
        
        try:
            self.decoder_path = self.compile_decoder()
        except Exception as e:
            print(f"初始化解码器失败: {e}")
            return False
        
        audio_extensions = ['.amr', '.silk', '.slk']
        audio_files = []
        for ext in audio_extensions:
            audio_files.extend([f for f in target_dir.rglob(f"*{ext}") if f.exists() and f.is_file()])
        
        total_files = len(audio_files)
        if total_files == 0:
            print("未找到任何可转换的语音文件")
            return True
        
        print(f"找到 {total_files} 个语音文件，开始处理...")
        success_count = 0
        
        def get_file_size(file_path: Path) -> int:
            try:
                return file_path.stat().st_size
            except:
                return 0
        
        audio_files.sort(key=get_file_size, reverse=True)
        
        if total_files <= 5:
            batch_size = total_files
        elif total_files <= 20:
            batch_size = min(5, config.MAX_WORKERS)
        else:
            batch_size = config.MAX_WORKERS
        
        print()
        with tqdm(total=total_files, 
                 bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}]",
                 desc="转换进度") as pbar:
            
            for i in range(0, total_files, batch_size):
                chunk = audio_files[i:i+batch_size]
                chunk_sizes = [get_file_size(f) for f in chunk]
                avg_size = sum(chunk_sizes) / len(chunk_sizes) if chunk_sizes else 0
                
                adjusted_workers = batch_size if avg_size < 10*1024*1024 else max(1, batch_size // 2)
                
                with ThreadPoolExecutor(max_workers=adjusted_workers) as executor:
                    futures = {
                        executor.submit(self.convert_single_file, f): f 
                        for f in chunk
                    }
                    for future in as_completed(futures):
                        if future.result():
                            success_count += 1
                        pbar.update(1)
        
        deleted_count = self._delete_processed_files()
        
        self._save_hash_cache()
        
        print()
        print(f"转换完成 | 成功: {success_count}/{total_files} 个 | 已删除原文件: {deleted_count} 个")
        return True

class QQAudioMonitor:
    """QQ语音文件监控器简化版"""
    
    def __init__(self, custom_voice_path: Optional[str] = None, output_root_dir: Optional[str] = None):
        self.qq_process_names = ["QQ", "QQMac", "QQ For Mac", "QQ Helper", "QQ Helper (Renderer)", "QQ Helper (GPU)"]
        self.previous_playing_files = set()
        self.custom_voice_path = custom_voice_path
        self.audio_extensions = {'.slk', '.silk', '.amr', '.mp3', '.m4a', '.wav', '.aac'}
        self.copy_history: Dict[str, tuple] = {}
        self.output_root_dir = self._init_output_root_dir(output_root_dir)
        self.output_date_dir: Optional[Path] = None
        self.file_counter = 1
        self.keyboard_listener = KeyboardListener()
        self.converter = QQVoiceConverter()
        self.qq_running_cache = False
        self.last_process_check = 0
        self.process_check_interval = config.PROCESS_CHECK_INTERVAL
        self.first_detection_done = False
        self._last_status = ""
        self.last_file_check_time = 0
        self.file_check_interval = config.CAPTURE_CHECK_INTERVAL
        self.recently_added_files = set()
        self._last_capture_time = 0
        self._capture_delay = config.CAPTURE_CHECK_INTERVAL
        self._consecutive_captures = 0
        
        self.qq_voice_patterns = [
            r".*QQ.*Audio.*",
            r".*temp.*audio.*",
            r".*voice.*",
            r".*silk.*",
            r".*amr.*",
            r".*slk.*"
        ]

    def _init_output_root_dir(self, output_root_dir: Optional[str]) -> Path:
        """初始化输出根目录"""
        if not output_root_dir:
            return config.DEFAULT_OUTPUT_ROOT
        else:
            output_root_dir = Path(output_root_dir).expanduser()
            output_root_dir.mkdir(parents=True, exist_ok=True)
            return output_root_dir
        
    def _create_date_subdir(self, first_file_path: str) -> Path:
        """根据第一个备份文件的创建日期建立子目录"""
        try:
            first_file_stat = os.stat(first_file_path)
            first_file_date = datetime.fromtimestamp(first_file_stat.st_ctime).strftime("%Y-%m-%d")
        except (OSError, ValueError) as e:
            logging.warning(f"无法获取文件 {first_file_path} 的创建时间，使用当前日期: {e}")
            first_file_date = datetime.now().strftime("%Y-%m-%d")
        
        date_subdir = self.output_root_dir / first_file_date
        date_subdir.mkdir(exist_ok=True)
        print(f"日期子目录: {date_subdir}")
        
        self.file_counter = self._get_initial_counter(date_subdir)
        return date_subdir
    
    def _get_initial_counter(self, target_dir: Path) -> int:
        """获取目标目录的初始序号"""
        if not target_dir.exists():
            return 1
        
        seq_pattern = re.compile(r'^(\d{2})\..+')
        max_seq = 0
        
        for file in target_dir.iterdir():
            if file.is_file():
                match = seq_pattern.match(file.name)
                if match:
                    seq = int(match.group(1))
                    if seq > max_seq:
                        max_seq = seq
        
        return max_seq + 1
    
    def _generate_target_path(self, original_path: str) -> Optional[Path]:
        """生成目标文件路径"""
        if not self.output_date_dir:
            self.output_date_dir = self._create_date_subdir(original_path)
        
        ext = Path(original_path).suffix.lower()
        seq_str = f"{self.file_counter:02d}"
        target_filename = f"{seq_str}{ext}"
        target_path = self.output_date_dir / target_filename
        
        self.file_counter += 1
        return target_path
    
    def _copy_file(self, original_path: str) -> Optional[str]:
        """复制文件并记录历史"""
        if original_path in self.recently_added_files:
            return None
        
        if original_path in self.copy_history:
            return self.copy_history[original_path][0]
        
        try:
            if not os.path.exists(original_path):
                logging.warning(f"源文件不存在: {original_path}")
                return None
            
            target_path = self._generate_target_path(original_path)
            if not target_path:
                return None
            
            shutil.copy2(original_path, target_path)
            backup_time = datetime.now()
            self.copy_history[original_path] = (str(target_path), backup_time)
            self.recently_added_files.add(original_path)
            threading.Timer(5, lambda: self.recently_added_files.discard(original_path)).start()
            
            print(f"捕获语音: {Path(original_path).name} → {target_path.name}")
            return str(target_path)
        except Exception as e:
            print(f"复制失败: {Path(original_path).name} - {e}")
            return None
    
    def _update_order_list(self) -> None:
        """更新播放文件顺序列表"""
        if not self.output_date_dir:
            return
        
        list_path = self.output_date_dir / "播放文件顺序列表.txt"
        
        try:
            sorted_items = sorted(
                self.copy_history.items(),
                key=lambda x: x[1][1]
            )
            
            with open(list_path, 'w', encoding='utf-8') as f:
                f.write(f"QQ语音文件备份顺序列表（{self.output_date_dir.name}）\n")
                f.write(f"更新时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
                f.write("=" * 60 + "\n")
                f.write(f"{'序号':<6} {'目标文件名':<15} {'原文件路径':<50} {'备份时间':<20} {'状态':<10}\n")
                f.write("-" * 120 + "\n")
                
                for idx, (original_path, (target_path, backup_time)) in enumerate(sorted_items, 1):
                    target_file = Path(target_path)
                    backup_time_str = backup_time.strftime('%Y-%m-%d %H:%M:%S')
                    status = "存在" if target_file.exists() else "已删除"
                    f.write(f"{idx:<6} {target_file.name:<15} {original_path:<50} {backup_time_str:<20} {status:<10}\n")
        except Exception as e:
            logging.error(f"更新顺序列表失败: {e}")

    def is_qq_running(self) -> bool:
        """检查QQ是否运行"""
        now = time.time()
        if now - self.last_process_check < self.process_check_interval:
            return self.qq_running_cache
        
        self.qq_running_cache = False
        try:
            for proc in psutil.process_iter(['name']):
                try:
                    proc_name = proc.info['name'] or ''
                    if any(qq_name.lower() in proc_name.lower() for qq_name in self.qq_process_names):
                        self.qq_running_cache = True
                        break
                except (psutil.NoSuchProcess, psutil.AccessDenied):
                    continue
        except Exception as e:
            logging.debug(f"进程检测错误: {e}")
        
        self.last_process_check = now
        return self.qq_running_cache

    def _is_qq_voice_file(self, file_path: str) -> bool:
        """判断是否为QQ语音文件"""
        path_lower = file_path.lower()
        
        ext = Path(file_path).suffix.lower()
        if ext not in self.audio_extensions:
            return False
        
        for pattern in self.qq_voice_patterns:
            if re.search(pattern, path_lower, re.IGNORECASE):
                return True
        
        try:
            file_size = os.path.getsize(file_path)
            if file_size < 100 or file_size > 10 * 1024 * 1024:
                return False
        except:
            return False
        
        return True

    def get_playing_audio_files(self) -> Set[str]:
        """检测正在播放的音频文件"""
        now = time.time()
        time_since_last_check = now - self.last_file_check_time
        
        if time_since_last_check < self._capture_delay:
            return set()
        
        self.last_file_check_time = now
        
        playing_files = set()
        
        try:
            result = subprocess.run(
                ['lsof', '-c', 'QQ'],
                capture_output=True, text=True, timeout=3
            )
            
            for line in result.stdout.split('\n'):
                if 'r' not in line:
                    continue
                    
                for ext in self.audio_extensions:
                    if ext in line:
                        match = re.search(rf'(/.*?{re.escape(ext)})', line)
                        if match:
                            file_path = match.group(1)
                            if (os.path.exists(file_path) and 
                                self._is_qq_voice_file(file_path)):
                                playing_files.add(file_path)
                        break
        
        except (subprocess.TimeoutExpired, FileNotFoundError, subprocess.SubprocessError) as e:
            logging.debug(f"文件检测失败: {e}")
        
        now = time.time()
        time_since_last_capture = now - self._last_capture_time
        
        if len(playing_files) > 0:
            self._consecutive_captures += 1
            self._last_capture_time = now
            
            if self._consecutive_captures > 2:
                self._capture_delay = max(0.1, self._capture_delay * 0.8)
        else:
            self._consecutive_captures = max(0, self._consecutive_captures - 1)
            
            if time_since_last_capture > 10:
                self._capture_delay = config.CAPTURE_CHECK_INTERVAL
        
        return playing_files

    def _convert_audio_files(self):
        """转换音频文件"""
        if not self.output_root_dir.exists():
            print("输出目录不存在，无法转换")
            return False
        
        print("开始转换音频文件...")
        success = self.converter.process_conversion(self.output_root_dir)
        if success:
            self._update_order_list()
        return success

    def start_monitoring(self, check_interval: int = 2):
        """启动监控"""
        self.keyboard_listener.start()
        
        print(f"\n操作指南:")
        print(f"  f - 立即转换音频文件")
        print(f"  q - 退出程序")
        print(f"  Ctrl+C - 强制退出")
        print(f"\n已优化鼠标点击播放语音的捕获效率")
        print(f"   检测间隔: {config.CAPTURE_CHECK_INTERVAL}秒")
        print(f"   最大延迟: {config.MAX_CAPTURE_DELAY}秒")
        print()
        
        try:
            while True:
                if self.keyboard_listener.has_key('q'):
                    print(f"\n收到退出指令...")
                    break
                if self.keyboard_listener.has_key('f'):
                    print()
                    self._convert_audio_files()
                    self.keyboard_listener.get_key()
                    print()
                
                current_interval = check_interval if self.is_qq_running() else 10
                
                if self.is_qq_running():
                    playing_files = self.get_playing_audio_files()
                    
                    if not self.first_detection_done:
                        self.previous_playing_files = playing_files
                        self.first_detection_done = True
                        print("已完成首次文件检测，忽略历史文件")
                    else:
                        new_playing_files = playing_files - self.previous_playing_files
                        new_target_files = []
                        
                        if new_playing_files:
                            print()
                            print(f"捕获到 {len(new_playing_files)} 个正在播放的语音文件!")
                            
                            for file_path in new_playing_files:
                                target_path = self._copy_file(file_path)
                                if target_path:
                                    new_target_files.append(target_path)
                        
                        if new_target_files:
                            self._update_order_list()
                        
                        self.previous_playing_files = playing_files
                
                else:
                    status_msg = f"QQ未运行，等待启动... | 已备份: {len(self.copy_history)}"
                    print(f"\r{status_msg}", end="", flush=True)
                    self._last_status = status_msg
                    self.previous_playing_files = set()
                    self.first_detection_done = False
                
                if self.is_qq_running() and self.first_detection_done:
                    date_dir_info = self.output_date_dir.name if self.output_date_dir else "未生成"
                    status_msg = (f"监控中... | "
                                 f"正在播放: {len(playing_files)} | "
                                 f"已备份: {len(self.copy_history)} | "
                                 f"检测延迟: {self._capture_delay:.2f}s | "
                                 f"当前目录: {date_dir_info}")
                    print(f"\r{status_msg}", end="", flush=True)
                    self._last_status = status_msg
                
                time.sleep(current_interval)
                
        except KeyboardInterrupt:
            print(f"\n监控已停止")
        except Exception as e:
            print(f"\n监控错误: {e}")
            logging.error(f"监控错误: {e}", exc_info=True)
        finally:
            self.keyboard_listener.stop()
            if self.copy_history:
                self._update_order_list()
                if self.output_date_dir:
                    print(f"\n顺序列表已保存至: {self.output_date_dir / '播放文件顺序列表.txt'}")
                    print(f"输出目录: {self.output_date_dir}")
                    print(f"备份文件数量: {len(self.copy_history)}")

def main():
    """主函数"""
    parser = argparse.ArgumentParser(description='QQ语音文件处理工具 - 简化版')
    parser.add_argument('--input-dir', help='指定QQ语音文件目录（可选）')
    parser.add_argument('--output-root', help=f'指定输出根目录（可选，默认：{config.DEFAULT_OUTPUT_ROOT}）')
    parser.add_argument('--interval', type=int, default=2, help='检查间隔（秒，默认：2）')
    parser.add_argument('--convert-only', action='store_true', help='仅执行转换，不监控')
    parser.add_argument('--convert-dir', help=f'指定转换目录（用于仅转换模式，默认：{config.DEFAULT_OUTPUT_ROOT}）')
    args = parser.parse_args()
    
    print("QQ语音文件处理工具 - 简化版")
    
    # 检查依赖
    try:
        import psutil
    except ImportError:
        print("缺少依赖: psutil")
        print("请运行: pip install psutil")
        return
    try:
        from tqdm import tqdm
    except ImportError:
        print("缺少依赖: tqdm")
        print("请运行: pip install tqdm")
        return
    
    # 仅转换模式
    if args.convert_only:
        converter = QQVoiceConverter()
        convert_dir = Path(args.convert_dir or args.output_root or config.DEFAULT_OUTPUT_ROOT).expanduser()
        if not convert_dir.exists():
            print(f"转换目录不存在: {convert_dir}")
            return
        print(f"开始转换目录: {convert_dir}")
        success = converter.process_conversion(convert_dir)
        if success:
            print("转换完成!")
        else:
            print("转换失败!")
        return
    
    # 监控模式
    monitor = QQAudioMonitor(
        custom_voice_path=args.input_dir,
        output_root_dir=args.output_root
    )
    
    print(f"输出目录: {monitor.output_root_dir}")
    try:
        monitor.start_monitoring(check_interval=args.interval)
    except Exception as e:
        print(f"程序错误: {e}")

if __name__ == "__main__":
    main()
